package dbs

import (
	"context"
	"encoding/json"
	"log"

	"github.com/qiniu/qmgo"
	"github.com/qiniu/qmgo/options"
)

type MongoModel struct {
	ctx  context.Context
	db   *qmgo.Database
	name string
}

var Mongo *MongoModel

func InitMongo(url, database string, opts ...options.ClientOptions) {
	model, err := NewModel(url, database, opts...)
	if err != nil {
		log.Fatal(err)
	}

	Mongo = model
}

func NewModel(url, database string, opts ...options.ClientOptions) (*MongoModel, error) {
	ctx := context.Background()
	cli, err := qmgo.NewClient(ctx, &qmgo.Config{Uri: url}, opts...)
	if err != nil {
		return nil, err
	}

	db := cli.Database(database)

	return &MongoModel{
		ctx,
		db,
		database,
	}, nil
}

func (m *MongoModel) Insert(coll string, doc interface{}, opts ...options.InsertOneOptions) (err error) {
	_, err = m.db.Collection(coll).InsertOne(m.ctx, doc, opts...)
	m.log("Insert", err, doc)
	return
}

func (m *MongoModel) Remove(coll string, filter interface{}, opts ...options.RemoveOptions) (err error) {
	err = m.db.Collection(coll).Remove(m.ctx, filter, opts...)
	m.log("Remove", err, filter)
	return
}
func (m *MongoModel) RemoveAll(coll string, filter interface{}, opts ...options.RemoveOptions) (err error) {
	_, err = m.db.Collection(coll).RemoveAll(m.ctx, filter, opts...)
	m.log("RemoveAll", err, filter)
	return
}

func (m *MongoModel) Update(coll string, filter, update interface{}, opts ...options.UpdateOptions) (err error) {
	err = m.db.Collection(coll).UpdateOne(m.ctx, filter, update, opts...)
	m.log("Update", err, update)
	return
}

func (m *MongoModel) UpdateAll(coll string, filter, update interface{}, opts ...options.UpdateOptions) (err error) {
	_, err = m.db.Collection(coll).UpdateAll(m.ctx, filter, update, opts...)
	m.log("UpdateAll", err, update)
	return
}

func (m *MongoModel) Upsert(coll string, filter, replacement interface{}, opts ...options.UpsertOptions) (err error) {
	_, err = m.db.Collection(coll).Upsert(m.ctx, filter, replacement, opts...)
	m.log("Upsert", err, replacement)
	return
}

func (m *MongoModel) Find(coll string, query interface{}, opts ...options.FindOptions) qmgo.QueryI {
	m.log("Find", nil, query)
	return m.db.Collection(coll).Find(m.ctx, query, opts...)
}

func (m *MongoModel) log(method string, err error, docs ...interface{}) {
	content, e := json.Marshal(docs)
	if e != nil {
		log.Println(err)
	} else if err != nil {
		log.Printf("mongo(%s) - %s - fail(%s) - %s \n", m.name, method, err.Error(), string(content))
	} else {
		log.Printf("mongo(%s) - %s - ok - %s \n", m.name, method, string(content))
	}
}
